package com.qo.qsdk.event;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public abstract class QSDKReceiver {
    static CopyOnWriteArrayList<WeakReference<QSDKReceiver>> mContainer = new CopyOnWriteArrayList<>();
    protected Map<Integer, IDispatcher> mEvents = new ConcurrentHashMap<>();
    private boolean mScaned;
    private WeakReference<QSDKReceiver> mThisWeakRef = new WeakReference<>(this);

    public interface Visitor<T> {
        void visit(T t, int i, Object... objArr);
    }

    public QSDKReceiver() {
        mContainer.add(0, mThisWeakRef);
    }

    public void detach() {
        mContainer.remove(mThisWeakRef);
    }

    void scanSubscriber(Object target) {
        List<Method> methods = searchDeclaredMethods(getClass(), QSDKReceiver.class);
        if (methods == null || methods.isEmpty()) {
            mScaned = true;
            return;
        }
        for (Method method : methods) {
            QSDKSubscribe subscribe = (QSDKSubscribe) method.getAnnotation(QSDKSubscribe.class);
            if (subscribe != null) {
                int[] eventIDs = subscribe.event();
                IDispatcher dispatcher = new Dispatcher(target, method);
                for (int eventID : eventIDs) {
                    if (!this.mEvents.containsKey(eventID)) {
                        this.mEvents.put(eventID, dispatcher);
                    }
                }
            }
        }
        mScaned = true;
    }

    private static List<Method> searchDeclaredMethods(Class<?> from, Class<?> end) {
        List<Method> list = null;
        while (from != end) {
            Method[] methods = from.getDeclaredMethods();
            if (methods != null && methods.length > 0) {
                if (list == null) {
                    list = new ArrayList<>(methods.length);
                }
                list.addAll(Arrays.asList(methods));
            }
            from = from.getSuperclass();
        }

        if (list == null) {
            return Collections.emptyList();
        }

        return list;
    }

    boolean hasScaned() {
        return mScaned;
    }

    protected boolean handle(int eventId, Object... args) {
        IDispatcher dispatcher = (IDispatcher) this.mEvents.get(eventId);
        if (dispatcher == null) {
            return false;
        }
        dispatcher.dispatch(args);
        return true;
    }

    static <T> void visit(Visitor<T> visitor, int eventID, Object... params) {
        Collection<WeakReference<QSDKReceiver>> carbin = null;
        for (WeakReference<QSDKReceiver> ref : mContainer) {
            QSDKReceiver target = (QSDKReceiver) ref.get();
            if (target == null) {
                if (carbin == null) {
                    carbin = new ArrayList<>();
                }
                carbin.add(ref);
            } else {
                visitor.visit((T) target, eventID, params);
            }
        }
        if (carbin != null) {
            mContainer.removeAll(carbin);
        }
    }
}
